import element.base.Word;
import element.base.Wordtype;

import java.util.ArrayList;

import static java.lang.Character.*;

public class Getsym {
    private String procedure;
    private ArrayList<Word> words;
    private int pointer;
    private char nowchar;
    private int lindex;

    public Getsym(String procedure) {
        this.procedure = procedure;
        this.words = new ArrayList<>();
        this.pointer = 0;
        this.lindex = 1;
    }

    //词法分析
    public void Execute() {
        getch();
        while (pointer < procedure.length()) {
            getSym();
        }
    }

    //取词
    public void getSym() {
        if (pointer < procedure.length() - 2 && procedure.substring(pointer - 1, pointer + 3).equals("main")) {
            isTK("main", Wordtype.MAINTK);
        } else if (pointer < procedure.length() - 3 && procedure.substring(pointer - 1, pointer + 4).equals("const")) {
            isTK("const", Wordtype.CONSTTK);
        } else if (pointer < procedure.length() - 6 && procedure.substring(pointer - 1, pointer + 7).equals("continue")) {
            isTK("continue", Wordtype.CONTINUETK);
        } else if (pointer < procedure.length() - 1 && procedure.substring(pointer - 1, pointer + 2).equals("int")) {
            isTK("int", Wordtype.INTTK);
        } else if (pointer < procedure.length() && procedure.substring(pointer - 1, pointer + 1).equals("if")) {
            isTK("if", Wordtype.IFTK);
        } else if (pointer < procedure.length() - 3 && procedure.substring(pointer - 1, pointer + 4).equals("break")) {
            isTK("break", Wordtype.BREAKTK);
        } else if (pointer < procedure.length() - 2 && procedure.substring(pointer - 1, pointer + 3).equals("else")) {
            isTK("else", Wordtype.ELSETK);
        } else if (pointer < procedure.length() - 3 && procedure.substring(pointer - 1, pointer + 4).equals("while")) {
            isTK("while", Wordtype.WHILETK);
        } else if (pointer < procedure.length() - 4 && procedure.substring(pointer - 1, pointer + 5).equals("getint")) {
            isTK("getint", Wordtype.GETINTTK);
        } else if (pointer < procedure.length() - 4 && procedure.substring(pointer - 1, pointer + 5).equals("printf")) {
            isTK("printf", Wordtype.PRINTFTK);
        } else if (pointer < procedure.length() - 4 && procedure.substring(pointer - 1, pointer + 5).equals("return")) {
            isTK("return", Wordtype.RETURNTK);
        } else if (pointer < procedure.length() - 2 && procedure.substring(pointer - 1, pointer + 3).equals("void")) {
            isTK("void", Wordtype.VOIDTK);
        } else if (nowchar == '!') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '=') {
                isSignal("!=", Wordtype.NEQ);
            } else {
                isSignal("!", Wordtype.NOT);
            }
        } else if (nowchar == '&') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '&') {
                isSignal("&&", Wordtype.AND);
            } else {
                error();
            }
        } else if (nowchar == '|') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '|') {
                isSignal("||", Wordtype.OR);
            } else {
                error();
            }
        } else if (nowchar == '+') {
            isSignal("+", Wordtype.PLUS);
        } else if (nowchar == '-') {
            isSignal("-", Wordtype.MINU);
        } else if (nowchar == '*') {
            isSignal("*", Wordtype.MULT);
        } else if (nowchar == '/') {
            isSignal("/", Wordtype.DIV);
        } else if (nowchar == '%') {
            isSignal("%", Wordtype.MOD);
        } else if (nowchar == '<') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '=') {
                isSignal("<=", Wordtype.LEQ);
            } else {
                isSignal("<", Wordtype.LSS);
            }
        } else if (nowchar == '>') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '=') {
                isSignal(">=", Wordtype.GEQ);
            } else {
                isSignal(">", Wordtype.GRE);
            }
        } else if (nowchar == '=') {
            if (pointer < procedure.length() && procedure.charAt(pointer) == '=') {
                isSignal("==", Wordtype.EQL);
            } else {
                isSignal("=", Wordtype.ASSIGN);
            }
        } else if (nowchar == ';') {
            isSignal(";", Wordtype.SEMICN);
        } else if (nowchar == ',') {
            isSignal(",", Wordtype.COMMA);
        } else if (nowchar == '(') {
            isSignal("(", Wordtype.LPARENT);
        } else if (nowchar == ')') {
            isSignal(")", Wordtype.RPARENT);
        } else if (nowchar == '[') {
            isSignal("[", Wordtype.LBRACK);
        } else if (nowchar == ']') {
            isSignal("]", Wordtype.RBRACK);
        } else if (nowchar == '{') {
            isSignal("{", Wordtype.LBRACE);
        } else if (nowchar == '}') {
            isSignal("}", Wordtype.RBRACE);
        } else if (nowchar == '"') {
            FormatString();
        } else if (isDigit(nowchar)) {
            IntConst();
        } else if (isAlphabetic(nowchar) || nowchar == '_') {
            Ident();
        } else if (isWhitespace(nowchar)) {
            if (nowchar == '\n') {
                lindex++;
            }
            getch();
        } else {
            error();
        }
    }

    //读取字符并记录
    public char getch() {
        return nowchar = procedure.charAt(pointer++);
    }

    public String setin(String content) {
        content += nowchar;
        getch();
        return content;
    }

    //回滚字符并记录
    public void rollback(int n) {
        for (int i = 0; i < n; i++) {
            pointer--;
        }
        nowchar = procedure.charAt(pointer - 1);
    }

    //判断是否为identifier_nondigit & digit
    public boolean isIdentElement(char ch) {
        return isDigit(ch) || isAlphabetic(ch) || ch == '_';
    }

    //Ident判断
    public void Ident() {
        String content = "";
        while (isIdentElement(nowchar)) {
            content = setin(content);
        }
        words.add(new Word(content, Wordtype.IDENFR, lindex));
    }

    //IntConst判断
    public void IntConst() {
        String content = "";
        while (isDigit(nowchar)) {
            content = setin(content);
        }
        words.add(new Word(content, Wordtype.INTCON, lindex));
    }

    //判断是否为FormatString中合法字符
    public boolean InFormat(char ch) {
        return ch == 32 || ch == 33 || ch == 37 || ch >= 40 && ch <= 126;
    }

    //FormatString判断
    public void FormatString() {
        boolean iserror = false;
        String content = "";
        if (nowchar == '"') {
            content = setin(content);
        } else {
            rollback(1);
        }
        while (nowchar != '"') {
            content += nowchar;
            if (!InFormat(nowchar)) {
                iserror = true;
            }
            if (nowchar == '\\') {
                if (getch() != 'n') {
                    iserror = true;
                } else {
                    content += nowchar;
                }
            }
            if (nowchar == '%') {
                if (getch() != 'd') {
                    iserror = true;
                } else {
                    content += nowchar;
                }
            }
            getch();
        }
        if (nowchar == '"') {
            content = setin(content);
        } else {
            iserror = true;

        }
        if (iserror) {
            words.add(new Word("error", Wordtype.STRCON, lindex));
        } else {
            words.add(new Word(content, Wordtype.STRCON, lindex));
        }
    }

    //关键字统一操作
    public void isTK(String token, Wordtype wordtype) {
        String content = "";
        int n = token.length();
        for (int i = 0; i < n; i++) {
            content = setin(content);
        }
        if (isIdentElement(nowchar)) {
            rollback(n);
            Ident();
        } else {
            words.add(new Word(content, wordtype, lindex));
        }
    }

    //符号统一操作
    public void isSignal(String signal, Wordtype wordtype) {
        String content = "";
        int n = signal.length();
        for (int i = 0; i < n; i++) {
            content = setin(content);
        }
        words.add(new Word(content, wordtype, lindex));
    }

    //错误处理接口
    public void error() {
        System.out.println("ERROR!");
        System.out.println(nowchar);
        System.exit(0);
    }

    //返回单词表
    public ArrayList<Word> getWords() {
        return words;
    }
}
